package com.lecyon.farm.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.lecyon.farm.base.BaseRequestPageVo;
import com.lecyon.farm.base.BaseRequestVo;
import com.lecyon.farm.base.BaseResponse;
import com.lecyon.farm.base.RestResponse;
import com.lecyon.farm.common.CommonService;
import com.lecyon.farm.common.constant.CommonConstant;
import com.lecyon.farm.common.constant.FarmSysConstant;
import com.lecyon.farm.common.enums.ExcelFileEnum;
import com.lecyon.farm.common.exception.FarmException;
import com.lecyon.farm.entity.*;
import com.lecyon.farm.mapper.FmSyncCarMapper;
import com.lecyon.farm.service.*;
import com.lecyon.farm.util.RedisUtils;
import com.lecyon.farm.util.ResultUtil;
import com.lecyon.farm.util.StringHelper;
import com.lecyon.farm.util.ValidateUtils;
import com.lecyon.farm.vo.add.AddFmSyncCarVo;
import com.lecyon.farm.vo.copy.DuplicateVo;
import com.lecyon.farm.vo.export.ExportFmSyncCarVo;
import com.lecyon.farm.vo.modify.ModifyFmSyncCarVo;
import com.lecyon.farm.vo.query.QueryFmCarVo;
import com.lecyon.farm.vo.query.QueryFmSyncCarVo;
import com.lecyon.farm.vo.request.IdsVo;
import com.lecyon.farm.vo.status.StatusVo;
import com.lecyon.farm.vo.view.FmSyncCarVo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 农机基本信息表 服务实现类
 *
 * @author YaoZheng
 * @since 2020-04-13
 */
@Service
@Validated
public class FmSyncCarServiceImpl extends ServiceImpl<FmSyncCarMapper, FmSyncCar> implements IFmSyncCarService {

    private static final Logger LOG = LoggerFactory.getLogger(FmSyncCarServiceImpl.class);

    private FmSyncCarMapper mapper;

    private IExcelService excelService;

    private RedisUtils redisUtils;

    private IFmSyncInformationService fmSyncInformationService;

    private IFmCarService fmCarService;

    private IFmCarInstallService fmCarInstallService;

    private IJsSysAreaService jsSysAreaService;

    /**
     * 读取同步条件设定信息，并开始同步主机信息
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RestResponse syncFmCar() {
        LOG.info("开始准备同步主机信息");
        List<FmSyncInformation> fmSyncInformationList = fmSyncInformationService.getSyncConfigInfo();
        if (CollectionUtils.isEmpty(fmSyncInformationList)) {
            LOG.warn("没有需要同步的设定信息");
            return ResultUtil.success(null, null);
        }
        redisUtils.lSet(FarmSysConstant.REDIS_KEY_SYNC_CONFIG, fmSyncInformationList);
        String areaCode, workType;
        QueryFmCarVo queryFmCarVo;
        String areaType;
        String parentCodes;
        String[] parentCode = new String[16];
        for (FmSyncInformation fmSyncInformation : fmSyncInformationList) {
            areaCode = fmSyncInformation.getAreaCode();
            workType = fmSyncInformation.getWorkType();
            if (StringHelper.isEmpty(areaCode)) {
                continue;
            }
            JsSysArea jsSysArea = jsSysAreaService.getById(areaCode);
            if (ValidateUtils.isNullOrEmpty(jsSysArea)) {
                continue;
            }
            queryFmCarVo = new QueryFmCarVo();
            parentCodes = jsSysArea.getParentCodes();
            if (StringHelper.isNotEmpty(parentCodes)) {
                parentCode = parentCodes.split(CommonConstant.HARDWARE_RESPONSE_SPLIT_STR);
            }
            if (StringHelper.isNotEmpty(jsSysArea.getAreaType()) && parentCode.length > 0) {
                areaType = jsSysArea.getAreaType();
                switch (areaType) {
                    case "1":
                        // 省级单位
                        queryFmCarVo.setProvince(areaCode);
                        break;
                    case "2":
                        // 市级单位
                        queryFmCarVo.setCity(areaCode);
                        queryFmCarVo.setProvince(parentCode[1]);
                        break;
                    default:
                        // 默认为“区/县”
                        queryFmCarVo.setCounty(areaCode);
                        queryFmCarVo.setProvince(parentCode[1]);
                        queryFmCarVo.setCity(parentCode[2]);
                }
            }
            QueryWrapper<FmCar> fmCarQueryWrapper = new QueryWrapper<>();
            if (StringHelper.isNotEmpty(queryFmCarVo.getProvince())) {
                fmCarQueryWrapper.eq("province", queryFmCarVo.getProvince());
            }
            if (StringHelper.isNotEmpty(queryFmCarVo.getCity())) {
                fmCarQueryWrapper.eq("city", queryFmCarVo.getCity());
            }
            if (StringHelper.isNotEmpty(queryFmCarVo.getCounty())) {
                fmCarQueryWrapper.eq("county", queryFmCarVo.getCounty());
            }
            if (StringHelper.isNotEmpty(workType)) {
                fmCarQueryWrapper.eq("work_type", workType);
            }
            List<FmCar> fmCarList = fmCarService.list(fmCarQueryWrapper);
            if (CollectionUtils.isNotEmpty(fmCarList)) {
                RestResponse<Long> transformResult;
                for (FmCar fmCar : fmCarList) {
                    // 将符合条件的FMCar 新增或更新到 fm_sync_car 表
                    transformResult = addOrModify(fmCar);
                    if (!transformResult.isSuccess()) {
                        throw new FarmException(transformResult.getCode(), transformResult.getErrorMessage());
                    }
                }
            }
        }
        return ResultUtil.success(null, null);
    }

    /**
     * 查询目前需要同步信息推送的主机号
     *
     * @return
     */
    @Override
    public List<FmSyncCarVo> getNeedSyncFmCarInfo() {
        List<FmSyncCarVo> fmSyncCarVoList;
        boolean flag = redisUtils.hasKey(FarmSysConstant.REDIS_KEY_NEED_SYNC_FM_CAR_TABLE);
        if (flag) {
            JSONArray jsonArray = (JSONArray) redisUtils.get(FarmSysConstant.REDIS_KEY_NEED_SYNC_FM_CAR_TABLE);
            LOG.info(jsonArray.toJSONString());
            fmSyncCarVoList = JSONObject.parseArray(jsonArray.toJSONString(), FmSyncCarVo.class);
            return fmSyncCarVoList;
        }
        // 查询 fm_sync_car 中待同步的主机
        QueryFmSyncCarVo queryFmSyncCarVo = new QueryFmSyncCarVo();
        queryFmSyncCarVo.setIsNeedSync(CommonConstant.WHETHER_TRUE);
        BaseRequestVo baseRequestVo = new BaseRequestVo();
        baseRequestVo.setEntity(queryFmSyncCarVo);
        RestResponse<List<FmSyncCarVo>> fmSyncCarResult = getList(baseRequestVo);
        if (!fmSyncCarResult.isSuccess()) {
            return null;
        }
        fmSyncCarVoList = fmSyncCarResult.getData();
        flag = redisUtils.set(FarmSysConstant.REDIS_KEY_NEED_SYNC_FM_CAR_TABLE, fmSyncCarVoList);
        if (!flag) {
            throw new FarmException(CommonConstant.EX_DB_REDIS_ERROR, "系统缓存异常！");
        }
        if (CollectionUtils.isNotEmpty(fmSyncCarVoList)) {
            for (FmSyncCarVo fmSyncCarVo : fmSyncCarVoList) {
                flag = redisUtils.hSet(FarmSysConstant.REDIS_KEY_NEED_SYNC_FM_CAR_MAP, fmSyncCarVo.getCarId(), fmSyncCarVo);
                if (!flag) {
                    throw new FarmException(CommonConstant.EX_DB_REDIS_ERROR, "系统缓存异常！");
                }
            }
        }
        return fmSyncCarVoList;
    }

    /**
     * 根据传入的 fmCar 判断是新增还是修改并进行相应操作
     *
     * @param fmCar
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RestResponse<Long> addOrModify(FmCar fmCar) {
        RestResponse<Long> result = new RestResponse<>();
        if (ValidateUtils.isNullOrEmpty(fmCar) || StringHelper.isEmpty(fmCar.getId())) {
            return ResultUtil.error(null, CommonConstant.EX_FORM_VALIDATE_REQUIRED, "缺少必要的查询条件 fmCarId");
        }
        String fmCarId = fmCar.getId();
        RestResponse<FmSyncCar> transformResult = getByFmCarId(fmCarId);
        if (!transformResult.isSuccess()) {
            return ResultUtil.error(null, transformResult.getCode(), transformResult.getErrorMessage());
        }
        if (ValidateUtils.isNullOrEmpty(transformResult.getData())) {
            // 未找到相应的数据 新增操作
            AddFmSyncCarVo addFmSyncCarVo = new AddFmSyncCarVo();
            BeanUtils.copyProperties(fmCar, addFmSyncCarVo);
            addFmSyncCarVo.setFmCarId(fmCar.getId());
            result = add(addFmSyncCarVo);
        } else {
            // 找到相应的数据 判断是否有修改 TODO
            FmSyncCar syncCar = transformResult.getData();
        }
        return ResultUtil.success(result, null);
    }

    /**
     * 根据 fmCarId 查找唯一FmSyncCar 记录
     *
     * @param fmCarId
     * @return
     */
    private RestResponse<FmSyncCar> getByFmCarId(@NotBlank String fmCarId) {
        QueryWrapper<FmSyncCar> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fm_car_id", fmCarId);
        int count = count(queryWrapper);
        if (1 < count) {
            LOG.warn("Too many records found with condition fm_car_id : {}", fmCarId);
            return ResultUtil.error(null, CommonConstant.TOO_MANY_RESULT_EXCEPTION, "找到多个符合条件的记录");
        }
        if (0 == count) {
            LOG.warn("Can not found any record with condition fm_car_id :{}", fmCarId);
            return ResultUtil.success(null, null);
        }
        FmSyncCar fmSyncCar = getOne(queryWrapper, true);
        return ResultUtil.success(null, fmSyncCar);
    }

    /**
     * 新增农机同步信息
     *
     * @param addVo 新增农机同步信息对象
     * @return 新增记录的id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RestResponse<Long> add(AddFmSyncCarVo addVo) {
        RestResponse<Long> result = new RestResponse<>();
        //有效性校验
        //必填项校验
        //重复性校验
        FmSyncCar entity = new FmSyncCar();
        BeanUtils.copyProperties(addVo, entity);
        //逻辑校验
        result = logicValidate(entity, result);
        if (!result.isSuccess()) {
            return result;
        }
        boolean flag = entity.insert();
        if (!flag) {
            LOG.warn("FmSyncCar add method invoke failed. vo :{}", addVo.toString());
            return ResultUtil.error(null, CommonConstant.EX_DB_INSERT_ERROR, "新增失败！");
        }
        LOG.info("FmSyncCar add method invoke success. vo : {}", addVo.toString());
        return ResultUtil.success(null, entity.getId());
    }

    @Override
    public RestResponse<Long> cumulative(AddFmSyncCarVo addVo) {
        return null;
    }

    /**
     * 编辑农机同步信息
     *
     * @param modifyVo 编辑对象
     * @return RestResponse
     */
    @Override
    public RestResponse<Long> modify(ModifyFmSyncCarVo modifyVo) {
        //有效性校验
        //必填项校验
        RestResponse<Long> result = new RestResponse<>();
        FmSyncCar entity = new FmSyncCar();
        BeanUtils.copyProperties(modifyVo, entity);
        //逻辑校验
        result = logicValidate(entity, result);
        if (!result.isSuccess()) {
            return result;
        }
        boolean flag = entity.updateById();
        if (!flag) {
            LOG.warn("FmSyncCar modify method invoke failed. vo :{}", modifyVo.toString());
            return ResultUtil.error(null, CommonConstant.EX_DB_UPDATE_ERROR, "编辑失败！");
        }
        LOG.info("FmSyncCar modify method invoke success. vo : {}", modifyVo.toString());
        return ResultUtil.success(null, entity.getId());
    }

    /**
     * 复制农机同步信息
     *
     * @param duplicateVo 复制对象Vo
     * @return
     */
    @Override
    public RestResponse<Long> duplicate(DuplicateVo duplicateVo) {
        return null;
    }

    @Override
    public BaseResponse changeStatus(StatusVo statusVo) {
        return null;
    }

    /**
     * 查看记录
     *
     * @param vo 查询条件(id)
     * @return 查询结果
     */
    @Override
    public RestResponse<FmSyncCarVo> get(QueryFmSyncCarVo vo) {
        RestResponse<FmSyncCarVo> result = new RestResponse<>();
        if (ValidateUtils.isNullOrEmpty(vo) || ValidateUtils.isNullOrEmpty(vo.getId())) {
            return ResultUtil.error(result, CommonConstant.EX_FORM_VALIDATE_REQUIRED, "缺少必要的查询条件！");
        }
        FmSyncCarVo resultVo = mapper.selectByPrimaryKey(vo);
        if (ValidateUtils.isNullOrEmpty(resultVo)) {
            return ResultUtil.error(result, CommonConstant.EX_DB_SELECT_MISS, "未找到符合条件的记录！");
        }
        return ResultUtil.success(result, resultVo);
    }

    /**
     * 查询列表
     *
     * @param vo 查询条件(包含分页）
     * @return 查询结果
     */
    @Override
    public RestResponse<Page<FmSyncCarVo>> getPageList(BaseRequestPageVo<QueryFmSyncCarVo> vo) {
        RestResponse<Page<FmSyncCarVo>> result = new RestResponse<>();
        QueryFmSyncCarVo queryCondition = vo.getEntity(QueryFmSyncCarVo.class);
        Page<FmSyncCarVo> page = new Page<>(vo.getPageNum(), vo.getPageSize());
        if (CollectionUtils.isNotEmpty(vo.getOrders())) {
            List<OrderItem> orderItemList = CommonService.batchAddPrefix4OrderColumn(vo.getOrders());
            page.setOrders(orderItemList);
        } else {
            page.setOrders(CommonConstant.orderItemList);
        }
        page = mapper.selectByPage(page, queryCondition);
        return ResultUtil.success(result, page);
    }

    /**
     * 查询列表
     *
     * @param vo 查询条件（不含分页信息）
     * @return 查询结果
     */
    @Override
    public RestResponse<List<FmSyncCarVo>> getList(BaseRequestVo<QueryFmSyncCarVo> vo) {
        RestResponse<List<FmSyncCarVo>> result = new RestResponse<>();
        QueryFmSyncCarVo queryCondition = vo.getEntity(QueryFmSyncCarVo.class);
        List<FmSyncCarVo> resultVoList = mapper.selectByCondition(queryCondition);
        List<String> carIdList = Lists.newLinkedList();
        if (CollectionUtils.isNotEmpty(resultVoList)) {
            resultVoList.stream().forEach(item -> carIdList.add(item.getCarId()));
        }
        List<FmCarInstall> fmCarInstallList;
        Map<String, String> fmCarInstallMap = null;
        if (CollectionUtils.isNotEmpty(carIdList)) {
            QueryWrapper<FmCarInstall> fmCarInstallQueryWrapper = new QueryWrapper<>();
            fmCarInstallQueryWrapper.in("id", carIdList);
            fmCarInstallList = fmCarInstallService.list(fmCarInstallQueryWrapper);
            if (CollectionUtils.isNotEmpty(fmCarInstallList)) {
                fmCarInstallMap = fmCarInstallList.stream().collect(Collectors.toConcurrentMap(FmCarInstall::getId, FmCarInstall::getSensor3));
            }
        }
        if (MapUtils.isNotEmpty(fmCarInstallMap) && CollectionUtils.isNotEmpty(resultVoList)) {
            String carId;
            String toolNumber;
            for (FmSyncCarVo fmSyncCarVo : resultVoList) {
                carId = fmSyncCarVo.getCarId();
                if (fmCarInstallMap.containsKey(carId)) {
                    toolNumber = fmCarInstallMap.get(carId);
                    fmSyncCarVo.setToolNumber(toolNumber);
                }
            }
        }
        return ResultUtil.success(result, resultVoList);
    }

    /**
     * 根据条件查询结果,并导出到Excel
     *
     * @param vo 查询条件
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RestResponse<String> exportExcel(BaseRequestVo<QueryFmSyncCarVo> vo) {
        RestResponse<String> result = new RestResponse<>();
        List<ExportFmSyncCarVo> resultList = Lists.newArrayList();
        RestResponse<List<FmSyncCarVo>> queryResult = getList(vo);
        List<FmSyncCarVo> voList = null;
        if (queryResult.isSuccess()) {
            voList = queryResult.getData();
        }
        if (CollectionUtils.isNotEmpty(voList)) {
            for (FmSyncCarVo temp : voList) {
                ExportFmSyncCarVo exportVo = new ExportFmSyncCarVo();
                BeanUtils.copyProperties(temp, exportVo);
                resultList.add(exportVo);
            }
        }
        String downLoadUrl;
        try {
            downLoadUrl = excelService.exportExcel(resultList, ExcelFileEnum.FM_WORK);
            if (StringHelper.isEmpty(downLoadUrl)) {
                return ResultUtil.success(result, downLoadUrl);
            }
        } catch (IOException e) {
            throw new FarmException(CommonConstant.EX_EXCEL_ERROR, e.getMessage());
        }
        return ResultUtil.success(result, downLoadUrl);
    }

    /**
     * 通用删除方法，包含批量删除与单条删除
     *
     * @param vo 删除条件
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse delete(@NotNull IdsVo vo) {
        if (ValidateUtils.isNullOrEmpty(vo.getId()) && CollectionUtils.isEmpty(vo.getIdsList())) {
            LOG.warn("FmSyncCar delete params is null.", this.getClass().getSimpleName());
            return ResultUtil.error(CommonConstant.EX_FORM_VALIDATE_REQUIRED, "缺少必要的参数！");
        }
        List<Long> idsList = vo.getIdsList();
        if (CollectionUtils.isNotEmpty(idsList)) {
            //如果idList不为空，优先进行批量删除
            return deleteByIds(idsList);
        }
        Long id = vo.getId();
        return deleteById(id);
    }

    /**
     * 删除农机同步信息
     *
     * @param id 主键ID
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse deleteById(Long id) {
        boolean flag = removeById(id);
        if (!flag) {
            LOG.error("FmSyncCar delete unsuccessful. id :{}", id);
            return ResultUtil.error(CommonConstant.EX_DB_DELETE_ERROR, "删除记录失败！");
        }
        return ResultUtil.success();
    }

    /**
     * 批量删除农机同步信息
     *
     * @param idList 主键ID列表
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse deleteByIds(List<Long> idList) {
        boolean flag = removeByIds(idList);
        if (!flag) {
            LOG.error("FmSyncCar delete batch unsuccessful. ids :{}", idList);
            return ResultUtil.error(CommonConstant.EX_DB_DELETE_ERROR, "批量删除失败");
        }
        return ResultUtil.success();
    }

    /**
     * 逻辑校验规则
     *
     * @param entity       被校验的实体类
     * @param restResponse 消息传递对象
     * @return 结果
     */
    private RestResponse logicValidate(FmSyncCar entity, RestResponse restResponse) {
        return ResultUtil.success(restResponse, null);
    }

    /**
     * 查询记录是否重复
     * 标准：作业编号
     *
     * @param wokSerial 唯一性条件
     * @return
     */
    private boolean isExist(String wokSerial) {
        if (StringHelper.isEmpty(wokSerial)) {
            return false;
        }
        int count = mapper.selectCount(new QueryWrapper<FmSyncCar>().eq("work_serial", wokSerial));
        return count > 0 ? true : false;
    }

    /**
     * ----------------------------- 分割线 以下是setter方法，用于替换field上的@Autowired -----------------------------
     **/

    @Autowired
    public void setMapper(FmSyncCarMapper mapper) {
        this.mapper = mapper;
    }

    @Autowired
    @Lazy
    public void setExcelService(IExcelService excelService) {
        this.excelService = excelService;
    }

    @Autowired
    @Lazy
    public void setRedisUtils(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    @Autowired
    @Lazy
    public void setFmSyncInformationService(IFmSyncInformationService fmSyncInformationService) {
        this.fmSyncInformationService = fmSyncInformationService;
    }

    @Autowired
    @Lazy
    public void setFmCarService(IFmCarService fmCarService) {
        this.fmCarService = fmCarService;
    }

    @Autowired
    @Lazy
    public void setFmCarInstallService(IFmCarInstallService fmCarInstallService) {
        this.fmCarInstallService = fmCarInstallService;
    }

    @Autowired
    @Lazy
    public void setJsSysAreaService(IJsSysAreaService jsSysAreaService) {
        this.jsSysAreaService = jsSysAreaService;
    }
}
